use crate::*;

// [<+|->]<value>\r\n
impl RespEncode for i64 {
    fn encode(self) -> Vec<u8> {
        let sign = if self > 0 { "+" } else { "-" };
        format!("{}{}\r\n", sign, self).into_bytes()
    }
}

// +OK\r\n
impl RespEncode for SimpleString {
    fn encode(self) -> Vec<u8> {
        format!("+{}\r\n", self.0).into_bytes()
    }
}

// -Error message\r\n
impl RespEncode for SimpleError {
    fn encode(self) -> Vec<u8> {
        format!("-{}\r\n", self.0).into_bytes()
    }
}


//_\r\n
impl RespEncode for RespNull {
    fn encode(self) -> Vec<u8> {
        "_\r\n".into()
    }
}

//*<number-of-elements>\r\n<element-1>...<element-n>
// *0\r\n
impl RespEncode for RespArray {
    fn encode(self) -> Vec<u8> {
        let len = self.0.len();
        let mut buf = Vec::new();
        buf.extend_from_slice(format!("*{}", len).as_bytes());

        for item in self.0 {
            buf.extend_from_slice(format!("\r\n{}", String::from_utf8(item.encode()).unwrap()).as_bytes());
        }

        buf
    }
}


impl RespEncode for RespFrame {
    fn encode(self) -> Vec<u8> {
        match self {
            RespFrame::SimpleString(str) => {
                str.encode()
            }

            RespFrame::Error(e) => {
                e.encode()
            }
            RespFrame::Null(n) => {
                n.encode()
            }


            // RespFrame.
            _ => {
                vec![]
            }
        }
    }
}


// #<t|f>\r\n
impl RespEncode for bool {
    fn encode(self) -> Vec<u8> {
        if self {
            "#t\r\n".into()
        } else {
            "#f\r\n".into()
        }
    }
}


// $<length>\r\n<data>\r\n
impl RespEncode for BulkString {
    fn encode(self) -> Vec<u8> {
        let data = self.0;

        let len = data.len();
        format!("${}\r\n{}\r\n", len, String::from_utf8(data).unwrap()).into_bytes()
    }
}

// ,[<+|->]<integral>[.<fractional>][<E|e>[sign]<exponent>]\r\n
impl RespEncode for f64 {
    fn encode(self) -> Vec<u8> {
        let mut buf = Vec::with_capacity(32);
        buf.extend_from_slice(format!(",{:+e}\r\n", self).as_bytes());
        buf
    }
}

// %<number-of-entries>\r\n<key-1><value-1>...<key-n><value-n>
impl RespEncode for RespMap {
    fn encode(self) -> Vec<u8> {
        let len = self.0.len();
        let mut buf = Vec::new();
        buf.extend_from_slice(format!("%{}", len).as_bytes());
        for (key, value) in self.0 {
            buf.extend_from_slice(format!("\r\n{}", key).as_bytes());
            buf.extend_from_slice(format!("\r\n{}", String::from_utf8(value.encode()).unwrap()).as_bytes())
        }

        buf
    }
}


// ~<number-of-elements>\r\n<element-1>...<element-n>

impl RespEncode for RespSet {
    fn encode(self) -> Vec<u8> {
        let len = self.0.len();
        let mut buf = Vec::new();
        buf.extend_from_slice(format!("~{}", len).as_bytes());
        for value in self.0 {
            buf.extend_from_slice(format!("\r\n{}", String::from_utf8(value.encode()).unwrap()).as_bytes())
        }

        buf
    }
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_encode_i64() {
        assert_eq!(i64::encode(123), b"+123\r\n");

        assert_eq!(i64::encode(-123), b"-123\r\n");

        assert_eq!(i64::encode(0), b"0\r\n");
    }
}
